package core;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ResourceBundle;

import jmf.AdministradorJMF;

public class AdministradorDeSonidos {
	ResourceBundle config = ResourceBundle.getBundle("param");

	private static AdministradorDeSonidos instancia;

	public static synchronized AdministradorDeSonidos getInstance() {
		if (instancia == null) {
			instancia = new AdministradorDeSonidos();
		}
		return instancia;
	}

	private AdministradorDeSonidos() {
		esquemas = this.cargarEsquemas(config.getString("ARCHIVO_ESQUEMAS"),
				config.getString("FLAG_IDENTIFICADOR_ESQUEMA"), config
						.getString("DIR_CONSIGNAS"));
		tests = this.cargarEsquemas(config.getString("ARCHIVO_TESTS"), config
				.getString("FLAG_IDENTIFICADOR_TEST"), config
				.getString("DIR_RESPUESTAS"));
		consignas = this.cargarSonidos(config.getString("DIR_CONSIGNAS"));
	}

	private ArrayList<EsquemaEvaluacion> esquemas;

	private ArrayList<Sonido> consignas;

	private ArrayList<EsquemaEvaluacion> tests;

	private String nombreUsuario;

	private AdministradorJMF jmf = AdministradorJMF.getInstance();

	private Thread thread;
	
	private boolean cancelarTest = false;

	private boolean testOK = false;
	
	public ArrayList<EsquemaEvaluacion> getTests() {
		return tests;
	}

	public void setTests(ArrayList<EsquemaEvaluacion> tests) {
		this.tests = tests;
	}

	public ArrayList<Sonido> getConsignas() {
		return consignas;
	}

	public void setConsignas(ArrayList<Sonido> consignas) {
		this.consignas = consignas;
	}

	public ArrayList<EsquemaEvaluacion> getEsquemas() {
		return esquemas;
	}

	public void setEsquemas(ArrayList<EsquemaEvaluacion> esquemas) {
		this.esquemas = esquemas;
	}

	public String getNombreUsuario() {
		return nombreUsuario;
	}

	public void setNombreUsuario(String nombreUsuario) {
		this.nombreUsuario = nombreUsuario;
	}

	public AdministradorJMF getJmf() {
		return jmf;
	}

	public void setJmf(AdministradorJMF jmf) {
		this.jmf = jmf;
	}
	
	private ArrayList<EsquemaEvaluacion> cargarEsquemas(String archivoEsquema,
			String identificadorEsquema, String directorioSonidos) {
		ArrayList<EsquemaEvaluacion> esquemasAux = new ArrayList<EsquemaEvaluacion>();
		try {
			BufferedReader b = new BufferedReader(
					new FileReader(archivoEsquema));

			String s;
			EsquemaEvaluacion esquema = null;

			while ((s = b.readLine()) != null && s.length()>0) {
				if (s.contains(identificadorEsquema)) {
					esquema = new EsquemaEvaluacion();
					esquema.setIdentificador(s.substring(identificadorEsquema
							.length()));
					if (esquemasAux == null) {
						esquemasAux = new ArrayList<EsquemaEvaluacion>();
					}
					esquemasAux.add(esquema);
				} else {
					Sonido sonido = new Sonido(s, new File(directorioSonidos
							+ s + ".wav"));
					esquema.getSonidos().add(sonido);
				}
			}
			b.close();
		} catch (FileNotFoundException e) {
			System.out.println("No se puede leer");
		} catch (IOException e) {
			System.out.println("Error al leer la linea");
		}
		return esquemasAux;
	}

	private ArrayList<Sonido> cargarSonidos(String directorio) {
		ArrayList<Sonido> sonidosAux = new ArrayList<Sonido>();
		File dirSonidos = new File(directorio);
		File[] files = dirSonidos.listFiles();
		if (files != null){
			for (File archSonido : files) {
				Sonido sonido = new Sonido(archSonido.getName().substring(0,
						archSonido.getName().indexOf(".wav")), archSonido);
				sonidosAux.add(sonido);
			}
		}
		return sonidosAux;
	}

	public void cargarConsignas() {
		consignas = cargarSonidos(config.getString("DIR_CONSIGNAS"));
	}

	public void guardarEsquemas(String archivoDestino,
			String identificadorEsquema, ArrayList<EsquemaEvaluacion> esquemas) {
		try {
			BufferedWriter b = new BufferedWriter(
					new FileWriter(archivoDestino));
			PrintWriter p = new PrintWriter(b);
			for (EsquemaEvaluacion esquema : esquemas) {
				p.println(identificadorEsquema + esquema.getIdentificador());
				for (Sonido sonido : esquema.getSonidos()) {
					p.println(sonido.getIdentificador());
				}
			}
			p.close();
		} catch (IOException e) {
			System.out.println("Error al grabar");
		}
	}

	public void guardarEsquemasConsignas() {
		guardarEsquemas(config.getString("ARCHIVO_ESQUEMAS"), config
				.getString("FLAG_IDENTIFICADOR_ESQUEMA"), esquemas);
	}

	public void guardarEsquemasTests() {
		guardarEsquemas(config.getString("ARCHIVO_TESTS"), config
				.getString("FLAG_IDENTIFICADOR_TEST"), tests);
		tests = this.cargarEsquemas(config.getString("ARCHIVO_TESTS"), config
				.getString("FLAG_IDENTIFICADOR_TEST"), config
				.getString("DIR_RESPUESTAS"));
	}

	public void crearNuevoEsquema() {
		EsquemaEvaluacion esquema = new EsquemaEvaluacion();
		esquema.setIdentificador("e" + (esquemas.size() + 1));
		esquemas.add(esquema);
	}

	public boolean eliminarConsigna(Sonido consigna) {
		// "Borro" la consigna (la paso a un directorio de borradas
		File dir = new File(config.getString("DIR_BORRADO"));
		if (!dir.isDirectory())
			dir.mkdir();

		new File(dir, consigna.getArchivo().getName()).delete();
		boolean borrado = consigna.getArchivo().renameTo(
				new File(dir, consigna.getArchivo().getName()));

		if (borrado) {
			// Saco la consigna de la coleccion de consignas
			consignas.remove(consigna);

			// Chequeo que esa consigna no este en algun esquema y grabo.
			for (EsquemaEvaluacion esquema : esquemas) {
				esquema.getSonidos().remove(consigna);
			}
			guardarEsquemasConsignas();
		}

		return borrado;
	}

	public void grabarRespuesta(String nombreArchivo) {
		validarCreacionDirectorio(config.getString("DIR_RESPUESTAS"));
		jmf.grabar(config.getString("DIR_RESPUESTAS") + nombreArchivo, true);
	}

	public void grabarConsigna() {
		int valor = consignas.size() + 1;
		validarCreacionDirectorio(config.getString("DIR_CONSIGNAS"));
		jmf.grabar(config.getString("DIR_CONSIGNAS") + "s" + valor, false);
	}

	private void validarCreacionDirectorio(String directorio) {
		File dir = new File(directorio);
		if (!dir.isDirectory()){
			dir.mkdirs();
		}
	}

	public void grabarTest(final EsquemaEvaluacion esquema) {
		if (!jmf.isGrabacionEnCurso() && !jmf.isReproduccionEnCurso()) {
			if (nombreUsuario == null)
				throw new IllegalStateException();
			thread = (new Thread() {
				public void run() {
					cancelarTest = false;
					testOK = false;
					ArrayList<Sonido> sonidos = esquema.getSonidos();

					EsquemaEvaluacion test = new EsquemaEvaluacion();
					test.setIdentificador(getNombreUsuario());

					Iterator<Sonido> sonidosIt = sonidos.iterator();

					while (sonidosIt.hasNext() && !cancelarTest) {
						Sonido sonido = (Sonido) sonidosIt.next();
						jmf.reproducirSerie(sonido.getArchivo());

						if (!cancelarTest){
							String nombreConsigna = nombreUsuario
									+ esquema.getIdentificador()
									+ sonido.getIdentificador();
							grabarRespuesta(nombreConsigna);
							test.getSonidos().add(new Sonido(nombreConsigna, null));
	
							try {
								synchronized (this) {
									wait();
								}
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							jmf.detenerGrabacion();
						}
					}

					if (!cancelarTest){
						tests.add(test);
						guardarEsquemasTests();
						testOK = true;
					}
				}
			});
			thread.start();
		}
	}

	public void siguienteConsigna() {
		synchronized (thread) {
			thread.notify();
		}
	}
	
	public void cancelarTest() {
		cancelarTest = true;
		jmf.detenerReproduccion();
		synchronized (thread) {
			thread.notify();
		}
	}
	
	public boolean isTestOK(){
		return testOK;
	}
	
	public void establecerRuido(){
		jmf.establecerBaseRuido();
	}
}
